﻿using System.Collections.Generic;
using UnityEngine;
using System;
using ZyGame.Datable;

namespace ZyGame.Cache
{
    class DataCacheing : Singleton<DataCacheing>
    {
        private List<CacheObject> _cacheDataList = new List<CacheObject>();

        public DataCacheing()
        {
            Client.Tools.AddUpdate(OnUpdate);
        }

        private void OnUpdate()
        {
            if (_cacheDataList.Count == 0)
            {
                return;
            }

            for (int i = _cacheDataList.Count - 1; i >= 0; i--)
            {
                CacheObject assetCache = _cacheDataList[i];
                if (!assetCache.HasUnload())
                {
                    continue;
                }

                _cacheDataList.Remove(assetCache);
                assetCache.Dispose();
            }
        }

        public void Enqueue(IDatable context)
        {
            //Client.Console.WriteLine("[ENQUEUE CACHE]" + context.name);
            CacheObject cache = _cacheDataList.Find(x => x.context.name == context.name);
            if (cache != null)
            {
                cache.Refresh();
                return;
            }
            _cacheDataList.Add(new CacheObject(context));
        }

        public bool TryGetAndRemove<T>(string name, out T result) where T : IDatable
        {
            CacheObject cache = _cacheDataList.Find(x => x.context.name == name);
            if (cache == null)
            {
                result = default;
                return false;
            }
            Client.Console.WriteLine("[DEQUEUE CACHE]" + name);
            _cacheDataList.Remove(cache);
            result = (T)cache.context;
            cache.context = null;
            return true;
        }

        public bool TryGetAndRemove<T>(Func<T, bool> func, out T result) where T : class, IDatable
        {
            for (int i = 0; i < _cacheDataList.Count; i++)
            {
                T temp = _cacheDataList[i].context as T;
                if (temp is null)
                {
                    continue;
                }
                if (func(temp) is false)
                {
                    continue;
                }
                Client.Console.WriteLine("[DEQUEUE CACHE]" + temp.name);
                _cacheDataList.Remove(_cacheDataList[i]);
                result = (T)temp;
                _cacheDataList[i].context = null;
                return true;
            }
            result = default;
            return false;
        }

        class CacheObject : IDisposable
        {
            private float time;
            public IDatable context;

            public CacheObject(IDatable context)
            {
                this.context = context;
                Refresh();
            }

            public bool HasUnload()
            {
                return Time.realtimeSinceStartup - time >= AppConfig.GetAssetCacheTime();
            }

            public void Refresh()
            {
                time = Time.realtimeSinceStartup;
            }

            public void Dispose()
            {
                context.Dispose();
                context = null;
                GC.SuppressFinalize(this);
            }
        }
    }
}